package com.yupfeg.livedata

import androidx.annotation.MainThread
import androidx.lifecycle.*

/**
 *
 * 可携带订阅消费状态的LiveData
 * * 同一个`StatusLiveData`在同一个[LifecycleOwner]作用域中只能订阅一次，相同作用域不允许再次调用。避免
 * 在[LifecycleOwner]作用域销毁时，会自动移除该订阅
 * * 订阅消费状态只能处于主线程，暂不支持主线程订阅
 * * 如果需要与生命周期不相关的，可调用[observeForever]
 *
 * @author yuPFeG
 * @date 2020/12/17
 */
open class StateLiveData<T> : EventLiveData<T>() {

    /**
     * 状态订阅者map
     * */
    private val mStatusObserverMap : HashMap<Int, ProcessedStateObserveWrapper>
        by lazy(LazyThreadSafetyMode.NONE){ HashMap() }

    /**是否正在分发消费状态*/
    private var mDispatchingState = false
    /**是否处于无效的分发消费状态阶段*/
    private var mDispatchStateInvalidated = false

    // <editor-fold desc="对外公开方法">

    /**
     * 订阅LiveData的数据变化与消费状态变化，仅允许下游接收到非空值
     * * 在kotlin下，方便外部调用
     * @param owner The LifecycleOwner which controls the observer
     * @param onEventChange 数据变化，如不需要LiveData本身数据变化，则传null
     * @param onStateChange 持有的数据消费状态变化，如需要LiveData的消费状态变化，则传null
     * */
    @Suppress("unused")
    @MainThread
    open fun observeProcessState(
        owner: LifecycleOwner,
        onEventChange: ((T) -> Unit)? = null,
        onStateChange : ((LiveDataState)->Unit)? = null
    ){
        //过滤相同作用域的重复订阅
        if (isOwnerObserveStatus(owner)) return

        val observerWrapper = ProcessedStateObserveWrapper(
            owner,mEventVersion.get(),
            object : ProcessedStatusObserver<T> {
            override fun onProcessedStatusChange(state: LiveDataState) {
                onStateChange?.invoke(state)
            }

            override fun onChanged(t: T?) {
                //仅允许非空数据传递
                t?:return
                onEventChange?.invoke(t)
            }

        })
        super.observe(owner,observerWrapper)
    }

    /**
     * 订阅数据变化与消费状态变化
     * * 仅能在主线程调用
     * @param owner 视图对象
     * @param observer 订阅回调
     * */
    @Suppress("unused")
    @MainThread
    fun observeProcessState(owner: LifecycleOwner, observer : ProcessedStatusObserver<T>){
        //过滤相同作用域的重复订阅
        if (isOwnerObserveStatus(owner)) return

        val observerWrapper = ProcessedStateObserveWrapper(owner,mEventVersion.get(),observer)
        super.observe(owner, observerWrapper)
    }

    /**
     * 移除消费状态的订阅对象
     * * 仅能在主线程调用
     * @param owner
     * */
    @Suppress("unused")
    @MainThread
    fun removeProcessStateObserver(owner: LifecycleOwner, observer : ProcessedStatusObserver<T>){
        if (!isOwnerObserveStatus(owner)) return

        val observerWrapper = ProcessedStateObserveWrapper(owner,mEventVersion.get(),observer)
        super.removeObserver(observerWrapper)
    }

    /**
     * 尝试指定[LifecycleOwner]作用域的的LiveData消费状态
     * @param owner [LifecycleOwner]
     * @return 如果[LifecycleOwner]是已订阅的作用域，则返回当前作用域的状态，否则返回null
     * */
    fun obtainProcessState(owner: LifecycleOwner) : LiveDataState?{
        if (!isOwnerObserveStatus(owner)) return null
        val storeId = getLifecycleOwnerId(owner)
        val observerWrapper = mStatusObserverMap[storeId]
        return observerWrapper?.currProcessState
    }

    // </editor-fold>

    // <editor-fold desc="赋值操作">

    /**
     * 所有的赋值操作最终都会调用该方法
     * @param value
     */
    override fun setValue(value: T?) {
        //分发待订阅状态
        dispatchingProcessStatus(LiveDataState.waitProcess())
        super.setValue(value)
    }

    // </editor-fold>

    /**
     * 订阅视图消费状态与数据变化的回调监听
     * */
    interface ProcessedStatusObserver<T> : Observer<T>{
        /**
         * LiveData状态变化时回调
         * */
        fun onProcessedStatusChange(state : LiveDataState)
    }

    /**
     * 获取[LifecycleOwner]的内存地址
     * @param owner
     * @return [LifecycleOwner.getLifecycle]系统内存地址，不会因为重写hashCode而变化
     * */
    @Suppress("MemberVisibilityCanBePrivate")
    protected open fun getLifecycleOwnerId(owner: LifecycleOwner) : Int{
        return System.identityHashCode(owner.lifecycle)
    }

    /**
     * 指定[LifecycleOwner]是否已订阅了LiveData消费状态
     * @param owner [LifecycleOwner]
     * */
    protected open fun isOwnerObserveStatus(owner: LifecycleOwner) : Boolean{
        val storeId = getLifecycleOwnerId(owner)
        return mStatusObserverMap.containsKey(storeId)
    }

    // <editor-fold desc="LiveData状态">

    /**
     * 分发liveData消费状态给所有状态订阅者
     * @param liveDataState liveData状态
     * */
    protected open fun dispatchingProcessStatus(liveDataState: LiveDataState){
        if (mStatusObserverMap.isEmpty()) return

        if (mDispatchingState){
            //避免未分发完，再次分发新的状态
            mDispatchStateInvalidated = true
            return
        }
        mDispatchingState = true
        do {
            // 未分发完再次触发分发事件，则将新的分发事件等待.
            // 直到上一次事件完成后，才会执行新的分发事件
            mDispatchStateInvalidated = false
            for (statusObserverWrapper in mStatusObserverMap.values) {
                statusObserverWrapper.onProcessedStatusChange(liveDataState)
                if (statusObserverWrapper.currProcessState is LiveDataState.Error){
                    //如果为出现异常状态，则在订阅者消费了错误后，
                    // 自动切换为已处理状态，避免外部手动调用，仅提示一次错误状态
                    statusObserverWrapper.currProcessState = LiveDataState.processed()
                }
                if (mDispatchStateInvalidated) {
                    break
                }
            }
        }while (mDispatchStateInvalidated)
        mDispatchingState = false
    }

    /**
     * 执行移除liveData状态订阅对象操作
     * @param owner [LifecycleOwner]
     * */
    protected fun performRemoveStatusObserver(owner: LifecycleOwner){
        if (mStatusObserverMap.isEmpty()) return

        val storeId = getLifecycleOwnerId(owner)
        if (mStatusObserverMap.containsKey(storeId)){
            mStatusObserverMap.remove(storeId)
        }
    }

    // </editor-fold>

    // <editor-fold desc="订阅包装类">

    /**
     * LiveData消费状态的订阅者装饰类
     * */
    private inner class ProcessedStateObserveWrapper(
        private val mOwner : LifecycleOwner,
        private var mLastVersion: Int,
        private val mObserver: ProcessedStatusObserver<in T>,
    ) : ProcessedStatusObserver<T>, LifecycleEventObserver{

        var currProcessState : LiveDataState = LiveDataState.Start
        val storeId : Int = getLifecycleOwnerId(mOwner)

        init {
            //仅添加新的Lifecycle作用域，过滤已订阅过的作用域
            if (!isOwnerObserveStatus(mOwner)){
                mStatusObserverMap[storeId] = this
                mOwner.lifecycle.addObserver(this)
            }
        }

        /**
         * Called when the data is changed.
         * @param t  The new data
         */
        override fun onChanged(t: T) {
            if (mEventVersion.get() <= mLastVersion) return
            //数据发送变化，更新版本号
            mLastVersion = mEventVersion.get()
            t?.also {
                //仅允许非空值向下游执行
                mObserver.onChanged(t)
                //分发已消费状态
                dispatchingProcessStatus(LiveDataState.processed())
            }
        }

        /**
         * 视图生命周期变化，仅用于在视图销毁时自动移除生命周期订阅对象
         * @param source The source of the event
         * @param event The event
         */
        override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
            var currentState = mOwner.lifecycle.currentState
            if (currentState == Lifecycle.State.DESTROYED) {
                //视图已销毁，移除订阅对象
                removeStatusObserver()
                return
            }
            var prevState: Lifecycle.State? = null
            while (prevState != currentState) {
                //LiveData消费状态不需要在生命周期处于活跃状态时再次分发，移除再次分发给所有订阅者的操作
                prevState = currentState
                currentState = mOwner.lifecycle.currentState
            }
        }

        /**
         * LiveData状态变化回调
         * */
        override fun onProcessedStatusChange(state: LiveDataState) {
            currProcessState = state
            mObserver.onProcessedStatusChange(state)
        }

        /**
         * 移除liveData状态订阅对象
         * */
        private fun removeStatusObserver(){
            mOwner.lifecycle.removeObserver(this)
            performRemoveStatusObserver(mOwner)
        }
    }

    // </editor-fold>
}